/**
 * $Revision$
 * $Date$
 *
 * This file is part of the Solveur repository software.
 * Copyright (C) 2012 Solveur developer team
 * See http://code.google.com/p/solveur/
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 **/

package gougeon.android.solveur;

//import java.text.Normalizer;
import java.text.Normalizer;
import java.util.List;
import java.util.Map;

import android.os.Handler;
import android.os.Message;
//import android.util.Log;



public class Chaine {

	private String	chaineData;
	private long	chaineId;

	private int		chainePointsTotal;
	private int		chainePoints[];
	private int		chaineNumMagicChar=0;

	private String	supportedBasicCharSet="ABCDEFGHIJKLMNOPQRSTUVWXYZ";
	private String	supportedExtendedCharSet="ÉÈÊËÀÂÎÏÛÇÔÙÜ";
	
	private String	supportedCharSet=supportedBasicCharSet+supportedExtendedCharSet;

	private int		supportedCharIds[]={2,		3,		5,		7,		11,		13,		17,		19,
										23,		29,		31,		37,		41,		43,		47,		53,
										59,		61,		67,		71,		73,		79,		83,		89, 
										97,		101,	11,		11,		11,		11,		2,		2,
										23,		23,		73,		5,		47,		73,		73};

	private int		supportedCharPoints[] = {	1,	3,	3,	2,	1,	4,	2,	4,
												1,	8,	10,	1,	2,	1,	1,	3,
												8,	1,	1,	1,	1,	4,	10,	10,	
												10,	10,	1,	1,	1,	1,	1,	1,
												1,	1,	1,	3,	1,	1,	1};

	public Chaine(String input_string)
	{
		int idx,char_id;
		char my_char;
		chaineData=input_string;
		chaineId=1;
		//adjustPoints=0;
		chainePointsTotal=0;
		chainePoints=new int[input_string.length()];
		//initial_input_priv=input_string;

		//TODO : fix this weird loop, l'invariant bordel !
		for (idx=0; idx<chaineData.length(); idx++)
		{
			my_char=chaineData.charAt(idx);
			if (my_char==' ')
			{
				String loc_chaine="";
				if (idx==0)
					loc_chaine=chaineData.substring(1, chaineData.length());
				else if (idx==chaineData.length()-1)
					loc_chaine=chaineData.substring(0,chaineData.length()-1);
				else
					loc_chaine=chaineData.substring(0, idx)+chaineData.substring(idx+1,chaineData.length());

				chaineData=loc_chaine;
				chaineNumMagicChar++;
			}
		}

		for (idx=0; idx<chaineData.length(); idx++)
		{
			my_char=chaineData.charAt(idx);

			if (supportedCharSet.indexOf(my_char)!=-1)
			{
				char_id=supportedCharIds[supportedCharSet.indexOf(my_char)];
				chaineId=chaineId*char_id;
				//chainePointsTotal+=supportedCharPoints[supportedCharSet.indexOf(my_char)];
				chainePoints[idx]=supportedCharPoints[supportedCharSet.indexOf(my_char)];
				chainePointsTotal+=chainePoints[idx];
			}
			else
			{
				chaineId=0;
				chainePointsTotal=0;
				chainePoints[idx]=0;
				//Log.e("solveur", "New chaine : invalid ! (\""+this.the_chaine+"\")");
			}
		}
		for (; idx<input_string.length(); idx++)
		{
			chainePoints[idx]=0;
		}
		/*String xxx= "";
		for (idx=0; idx<input_string.length(); idx++)
		{
			xxx+=" "+chainePoints[idx];
		}*/
		//Log.d("solveur","Chaine="+chaineData+" "+xxx);
		//Log.d("solveur", "New Chaine : \""+this.the_chaine+"\" (ART l="+the_chaine.length()+") ("+chaine_id+") num_magic_char="+num_magic_char);
	}
	
/*	public Chaine(String input_string, int adjustPoints)
	{
		// Call standard constructor
		this(input_string);
		// Update chainePoints
		this.adjustPoints=adjustPoints;
		//int idx;

		//Log.d("solveur","Chaine with points="+chaineData+" "+xxx+" / "+chainePointsTotal);
	}
*/
	public Chaine(String input_string, int chainePoints[])
	{
		// Call standard constructor
		this(input_string);
		// Update chainePoints
		this.chainePoints=chainePoints;
		int idx;
		//String xxx= "";
		chainePointsTotal=0;
		for (idx=0; idx<input_string.length(); idx++)
		{
		//	xxx+=" "+chainePoints[idx];
			chainePointsTotal+=chainePoints[idx];
		}
		//Log.d("solveur","Chaine with points="+chaineData+" "+xxx+" / "+chainePointsTotal);
	}
/*	public Chaine(String input_string, int chainePoints[], int chaineMap[])
	{
		this(input_string,chainePoints);
		int idx;
		String xxx= "";
		for (idx=0; idx<input_string.length(); idx++)
		{
			xxx+=" "+chaineMap[idx];
			//chainePointsTotal+=chaineMap[idx];
		}
		Log.d("solveur","Chaine with points dddd="+chaineData+" "+xxx+" / "+chainePointsTotal);
	}*/
	/**
	 * @return the chaine_id
	 */
	public long getChaine_id() {
		return chaineId;
	}

	public int getScore()
	{
		return chainePointsTotal;
	}
	/* (non-Javadoc)
	 * @see java.lang.Object#toString()
	 */
	public String toString()
	{
		return this.chaineData;
	}

	public int length()
	{
		return this.chaineData.length();
	}

	public int analyse(	Map<Integer,MotScore> liste_de_mots_trouves,
						int current_key[],
						Handler handler,
						int min_size,
						int max_size)
	{
		int jdx,pow=1;
		int myIdx;
		Message msg3 = new Message();
		for (jdx=0;jdx<chaineNumMagicChar;jdx++)
		{
			pow=pow*supportedBasicCharSet.length();
		}
		//Log.d("solveur",""+num_magic_char+" / "+pow);

		msg3.arg1=26*pow*(max_size-min_size+1);
		msg3.what=91;
		handler.sendMessage(msg3);

		int boardCharMapping[] = new int[chaineData.length()+1];
		
		boardCharMapping[0]=1;
		for (myIdx=1; myIdx<chaineData.length()+1; myIdx++)
		{
			boardCharMapping[myIdx]=0;
		}

		// Consider every possible char added to input (existing char on the board)
		for (myIdx=supportedBasicCharSet.length()-1;myIdx>=0; myIdx--)
		{
			// Create string with this char and keep its points
			//Chaine magic_chaine=new Chaine (supportedCharSet.charAt(idx)+chaineData,adjustPoints);

			Chaine magic_chaine=new Chaine (supportedCharSet.charAt(myIdx)+chaineData);
			magic_chaine.analyse_interne(	chaineNumMagicChar,
											chaineNumMagicChar,
											supportedCharSet.charAt(myIdx),
											//initial_input_priv,
											liste_de_mots_trouves,
											current_key,
											handler,
											min_size,
											max_size,
											boardCharMapping);
		}
		return 0;
	}

	private int analyse_interne(int loc_num_magic_char,
								int total_magic_char,
								char value_of_board_char,
								//String initial_input,
								Map<Integer,MotScore> liste_de_mots_trouves,
								int current_key[],
								Handler handler,
								int min_size,
								int max_size,
								int boardCharMapping[])
	{
		int idx;
		//Log.d("solveur", "analyse("+loc_num_magic_char+")");

		if (loc_num_magic_char==0)
		{
			
			
			for (idx=max_size;idx>=min_size; idx--)
			{

				handler.sendEmptyMessage(92);

				combination("",chaineData, idx,
							total_magic_char,
							value_of_board_char,
							//0,//adjustPoints,
							new int[1],
							chainePoints,
							//initial_input,
							liste_de_mots_trouves,
							current_key,
							new int [1],
							boardCharMapping);
			}
		}
		else
		{
			for (idx=supportedBasicCharSet.length()-1;idx>=0; idx--)
			{
				// Create string with this char and decrease its points by amount of points of added char.
				int magicChainePoints[] = new int[chaineData.length()+1];
				int myIdx;
				magicChainePoints[0]=0;
				for (myIdx=1;myIdx<chaineData.length()+1;myIdx++)
				{
					magicChainePoints[myIdx]=chainePoints[myIdx-1];
				}

				int subBoardCharMapping[] = new int[chaineData.length()+1];
				subBoardCharMapping[0]=2;
				//String Xx=" "+subBoardCharMapping[0];
				for (myIdx=1; myIdx<chaineData.length()+1; myIdx++)
				{
					subBoardCharMapping[myIdx]=boardCharMapping[myIdx-1];
					//Xx+=" "+subBoardCharMapping[myIdx];
				}
				//Log.d("solveur","boardmap="+Xx);
				//Chaine magic_chaine=new Chaine (supportedCharSet.charAt(idx)+chaineData,magicChainePoints,subBoardCharMapping);
				Chaine magic_chaine=new Chaine (supportedCharSet.charAt(idx)+chaineData,magicChainePoints);
				magic_chaine.analyse_interne(	loc_num_magic_char-1,
												total_magic_char,
												value_of_board_char,
												//initial_input,
												liste_de_mots_trouves,
												current_key,
												handler,
												min_size,
												max_size,
												subBoardCharMapping);
			}
		}
		return 0;
	}


	public static void combination(	String head, 
									String tail, 
									int k,
									int total_magic_char,
									char value_of_existing_char,
									int headChainePoints[],
									int tailChainePoints[],
									Map<Integer,MotScore> liste_de_mots_trouves,
									int current_key[],
									int headBoardCharMapping[],
									int tailBoardCharMapping[])
	{
		if (tail.length()<k)  // we are unable to pick k letters from tail
		{
			return;  // no combination at all
		}
		else if (k==0)  // combination found
		{
			if (Mots.getMotsDeNLettresLoaded(head.length())==1)
			{
				//Log.d("solveur","hola:"+head+"/"+head.length());
				/*int subChainePoints[] = new int[head.length()];
				int myIdx;
				for (myIdx=0;myIdx<head.length();myIdx++)
				{
					subChainePoints[myIdx]=headChainePoints[myIdx];
				}*/
				//Log.d("solveur","boardmapXXX=");
				//Chaine sub_chaine= new Chaine(head,headChainePoints,headBoardCharMapping);
				Chaine sub_chaine= new Chaine(head,headChainePoints);
				sub_chaine.check_dico(	Mots.getMotsDeNLettres(sub_chaine.length()),
									 	liste_de_mots_trouves,
									 	current_key,
									 	total_magic_char,
									 	value_of_existing_char,
									 	headBoardCharMapping);
			}
		}
		else
		{
			String new_tail = tail.substring(1);
			String new_head = head+tail.charAt(0);
			int newHeadChainePoints[] = new int[new_head.length()];
			int newTailChainePoints[] = new int[new_tail.length()];
			int myIdx;

			int newTailBoardCharMapping[] = new int[new_tail.length()];
			int newHeadBoardCharMapping[] = new int[new_head.length()];
			
			//Log.d("solveur","new_tail="+new_tail+"("+new_tail.length()+") new_head="+new_head+"("+new_head.length()+")");
			for (myIdx=0; myIdx<head.length(); myIdx++)
			{
				//Log.d("solveur","myIdx" +myIdx);
				newHeadChainePoints[myIdx]=headChainePoints[myIdx];
				newHeadBoardCharMapping[myIdx]=headBoardCharMapping[myIdx];
			}
			if (new_head.length()>=1)
			{
				newHeadChainePoints[new_head.length()-1]=tailChainePoints[0];
				newHeadBoardCharMapping[myIdx]=tailBoardCharMapping[0];
			}
			
			for (myIdx=0; myIdx<new_tail.length(); myIdx++)
			{
				newTailChainePoints[myIdx]=tailChainePoints[myIdx+1];
				newTailBoardCharMapping[myIdx]=tailBoardCharMapping[myIdx+1];
			}
			//Log.d("solveur","="+head+"."+tail+" => "+new_head+"."+new_tail+" / "+head+"."+new_tail+" ;;;"+zozo);
			combination(new_head,new_tail, k-1,
						total_magic_char,
						value_of_existing_char,
						newHeadChainePoints,
						newTailChainePoints,
						liste_de_mots_trouves,
						current_key,
						newHeadBoardCharMapping,
						newTailBoardCharMapping);
			combination(head, new_tail, k,
						total_magic_char,
						value_of_existing_char,
						headChainePoints,
						newTailChainePoints,
						liste_de_mots_trouves,
						current_key,
						newHeadBoardCharMapping,
						newTailBoardCharMapping);
		}
	}

	void check_dico(	Map<String, List<String>> current_hash,
						Map<Integer,MotScore> liste_de_mots_trouves,
						int current_key[],
						int total_magic_char,
						char value_of_existing_char,
						int boardCharMapping[])
	{
		int idx;

		if (current_hash.containsKey(String.valueOf(chaineId)))
		{
			// Found a match !
			// Add each word of the dictionary matching this key
			// stop adding if any word of list already in results
			// (because then all others likely to be in as well)
			for (idx=0; idx<current_hash.get(String.valueOf(chaineId)).size(); idx++)
			{

				String currMot=current_hash.get(String.valueOf(chaineId)).get(idx);
				//String Xx=" "+boardCharMapping[0];
				int myIdx;
				int swappedBoardCharMapping[] = new int[chaineData.length()];
				for (myIdx=0; myIdx<chaineData.length(); myIdx++)
				{
					//Xx+=" "+boardCharMapping[myIdx];
				}
				// swap boardcharMapping ....
				String normalized = Normalizer.normalize(currMot, Normalizer.Form.NFD);
				String currMot2=normalized.replaceAll("[^\\p{ASCII}]", "");;
				for (myIdx=0; myIdx<chaineData.length(); myIdx++)
				{
					swappedBoardCharMapping[myIdx]=0;
				}
				for (myIdx=0; myIdx<chaineData.length(); myIdx++)
				{
					if ((boardCharMapping[myIdx]==2) || (boardCharMapping[myIdx]==1))
					{

						int thisPos=currMot2.indexOf(chaineData.charAt(myIdx));
						swappedBoardCharMapping[thisPos]=boardCharMapping[myIdx];
						if (thisPos==0)
						{
							currMot2="."+currMot2.substring(thisPos+1);
						}
						else
						{
							currMot2=currMot2.substring(0,thisPos)+"."+currMot2.substring(thisPos+1);
						}
						
					}
				}
				MotScore element = new MotScore(currMot,chainePointsTotal,swappedBoardCharMapping);
				//Log.d("Solveur","Adding \""+currMot+"\" ("+chainePointsTotal+")");

				//TODO : replace score in already found words to maximize scoring ...
				if (!liste_de_mots_trouves.containsValue(element))
				{
					// First word of this key does'nt exist in result list, add it (and followings)

					liste_de_mots_trouves.put(current_key[1]++, element);

					/*if (current_hash.get(String.valueOf(chaine_id)).get(0).indexOf(value_of_existing_char)==-1)
			  			Log.d("solveur","Found match to "+current_hash.get(String.valueOf(chaine_id)).get(idx)+" with "+total_magic_char+" blank chars (points="+chaine_points+")");
					else	
			  			Log.d("solveur","Found match to "+current_hash.get(String.valueOf(chaine_id)).get(idx)+" with "+total_magic_char+" blank chars and \""+value_of_existing_char+"\" on the board (points="+chaine_points+")");
						//Log.d("solveur","current_key="+current_key[0]+" Liste=("+liste_de_mots_trouves+")");
					*/
				}
				else
				{
					// This word was already in results, all others from this key are thus also there.
					break;
				}
			}		
		}
	}
}
